package Graph;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import javax.imageio.ImageIO;

import perspectives.Property;
import perspectives.Viewer2D;
import perspectives.PropertyManager.OpenFile;
import util.NodeLinkViewer;
import util.Util;


public class GraphViewer extends NodeLinkViewer {

	Graph graph;
	
	GraphDrawer drawer;
	
	int[] edgeSources;
	int [] edgeTargets;

	public GraphViewer(String name, GraphData g) {
		super(name);		
		
		graph = g.graph;
		
		setDirected(graph.getDirected());
		
		ArrayList<Integer> e1 = new ArrayList<Integer>();
		ArrayList<Integer> e2 = new ArrayList<Integer>();
		
                graph.getEdgesAsIndeces(e1, e2);
		
		edgeSources = new int[e1.size()];
		edgeTargets = new int[e2.size()];
		for (int i=0; i<e1.size(); i++)
		{
			edgeSources[i] = e1.get(i);
			edgeTargets[i] = e2.get(i);
		}
		
		drawer = new ForceGraphDrawer(graph);
				
		try {	
			
			OpenFile ff = new OpenFile();
			Property<OpenFile> p33 = new Property<OpenFile>("Load Positions");
			p33.setValue(ff);
			this.addProperty(p33);
			
			
			Property<Double> p = new Property<Double>("Simulation.K_REP");
			p.setValue(new Double(5000000.));
			((ForceGraphDrawer)drawer).k_rep = 5000000;
			this.addProperty(p);
			
			p = new Property<Double>("Simulation.K_ATT");
			p.setValue(new Double(100.));
			this.addProperty(p);
			
			p = new Property<Double>("Simulation.SPRING_LENGTH");
			p.setValue(new Double(30.));
			this.addProperty(p);			
			
			p = new Property<Double>("Simulation.MAX_STEP");
			p.setValue(new Double(100.));
			((ForceGraphDrawer)drawer).max_step = 100.;
			this.addProperty(p);
			
			Property<Boolean> p2= new Property<Boolean>("Simulation.Simulate");
			p2.setValue(new Boolean(false));
			this.addProperty(p2);			

			SaveFile f = new SaveFile();
			Property<SaveFile> p3 = new Property<SaveFile>("Save");
			p3.setValue(f);
			this.addProperty(p3);		
		} 
		catch (Exception e) {		
			e.printStackTrace();
		}
	}

	public void simulate() {
		
		boolean b = (Boolean)this.getProperty("Simulation.Simulate").getValue();
		if (b)
		{
			long t = new Date().getTime();
			drawer.iteration();
		}
	}
	
	public <T> void propertyUpdated(Property p, T newvalue)
	{
		if (p.getName() == "Simulation.K_REP")
			((ForceGraphDrawer)drawer).k_rep = (Double)newvalue;
		else if (p.getName() == "Simulation.K_ATT")
			((ForceGraphDrawer)drawer).k_att = (Double)newvalue;
		else if (p.getName() == "Simulation.SPRING_LENGTH")
			((ForceGraphDrawer)drawer).spring_length = (Double)newvalue;
		else if (p.getName() == "Simulation.MAX_STEP")
			((ForceGraphDrawer)drawer).max_step = (Double)newvalue;
		else if (p.getName() == "Save")
			this.save((File)newvalue);

		else if (p.getName() == "Load Positions")
		{
			ArrayList<String> nodes = graph.getNodes();
			
			try{
			 FileInputStream fstream = new FileInputStream(((OpenFile)newvalue).path);
			 DataInputStream in = new DataInputStream(fstream);
			 BufferedReader br = new BufferedReader(new InputStreamReader(in));
			 String s;
			 while ((s = br.readLine()) != null)
			 {
				s = s.trim();
				
				String[] split = s.split("\t");
				
				if (split.length < 2) continue;
				
				int index = nodes.indexOf(split[0].trim());
				if (index < 0)
				{
					String s2 = split[0].trim().toLowerCase();
					for (int i=0; i<nodes.size(); i++)
					{
						String s1 = nodes.get(i).toLowerCase();
						if (s1.equals(s2))
						{
							index = i;
							break;
						}
					}
					if (index < 0)
					continue;
				}
				
				String[] poss = split[1].split(",");
				int x = (int)Double.parseDouble(poss[0]);
				int y = -(int)Double.parseDouble(poss[1]);
				
				if (drawer != null)
				{
					drawer.setX(index, (int)(x * 1.335));
					drawer.setY(index, (int)(y * 1.335));
					
					this.setAspect(index, PointAspectType.RECT_LABEL_FIT);
				}
			 }
			 
			 in.close();
			}
			catch(Exception e)
			{
				
			}
		}		
		
		else 
			super.propertyUpdated(p,newvalue);
	}
	
	
	
	public void save(File f)
	{
		ArrayList<String> nodes = graph.getNodes();
		for (int i=0; i<nodes.size(); i++)
		{
			int x = (int)drawer.getX(i);
			int y = (int)drawer.getY(i);
			
			
			Property<Integer> px = graph.nodeProperty(nodes.get(i), "x");
			if (px == null)
			{
				px = new Property<Integer>("x");
				graph.addNodeProperty(nodes.get(i), px);
			}
			px.setValue(x);
			
			Property<Integer> py = graph.nodeProperty(nodes.get(i), "y");
			if (py == null)
			{
				py = new Property<Integer>("y");
				graph.addNodeProperty(nodes.get(i), py);
			}
			py.setValue(y);
						
		}		
		graph.toGraphML(f);		
	}
	
	public void load(File f)
	{
		graph.fromGraphML(f);
		
		ArrayList<String> nodes = graph.getNodes();
		for (int i=0; i<nodes.size(); i++)
		{
			Property<Integer> p = graph.nodeProperty(nodes.get(i), "x");
			if (p != null)
				drawer.setX(i,p.getValue().intValue());
			else
				drawer.setX(i,0);
			
			p = graph.nodeProperty(nodes.get(i), "y");
			if (p != null)
				drawer.setY(i,p.getValue().intValue());
			else
				drawer.setY(i,0);
					}
	}

	@Override
	protected String getNodeLabel(int p) {
		return graph.getNodes().get(p);
	}

	@Override
	protected int getNumberOfNodes() {
		return graph.numberOfNodes();
	}

	@Override
	protected int getNodeX(int p) {
		return (int)drawer.getX(p);
	}

	@Override
	protected int getNodeY(int p) {
		return (int)drawer.getY(p);
	}

	@Override
	protected void setNodeX(int p, int x) {
		drawer.setX(p, x);
	}

	@Override
	protected void setNodeY(int p, int y) {
		drawer.setY(p, y);
	}

	@Override
	protected int[] getEdgeSources() {
		return edgeSources;
	}

	@Override
	protected int[] getEdgeTargets() {
		return edgeTargets;
	}
	@Override
	public Color backgroundColor() {
		return new Color(230,230,230);		
	}	
	
}
